﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Proxies;
using System.Runtime.Remoting.Messaging;
using System.Reflection;
using System.Collections;

namespace PickGold.Util.Proxies
{
	/// <summary/>
	public class ClassProxy<REAL> : ClassProxy
	{
		/// <summary/>
		protected ClassProxy(object proxy)
			: base(typeof(REAL), proxy)
		{
		}

		/// <summary/>
		protected ClassProxy(Type type, object proxy)
			: base(type, proxy)
		{
		}


		/// <summary>
		/// 取代理或创造代理
		/// </summary>
		/// <param name="proxy">原型或代理</param>
		/// <param name="deep">小于等于零表示取原型，一表示一次代理，大于表示多次重复代理</param>
		/// <returns>代理或原型</returns>
		public static REAL GetProxy(object proxy, int deep)
		{
			return ClassProxy.GetProxy<REAL>(proxy, deep);
		}
	}

	/// <summary/>
	public class ClassProxy : RealProxy
	{
		private object _Proxy;

		/// <summary/>
		protected ClassProxy(Type type, object proxy)
			: base(type)
		{
			this._Proxy = proxy;
		}

		/// <summary/>
		public override IMessage Invoke(IMessage msg)
		{
			var mcm = msg as IMethodCallMessage;
			if (mcm == null)
				return new ReturnMessage(null, null, 0, null, null);


			var o = this._Proxy;
			var e = Reflector<InvokeMessageEventArgs>.DefaultValue;
			var ps = mcm.MethodBase.GetParameters();
			if (ClassProxy.ProxyInvoke != null)
			{
				e = new InvokeMessageEventArgs(mcm);
				ClassProxy.ProxyInvoke(o, e);
				if (e.ReturnMessage != null)
					return e.ReturnMessage;
			}

			try
			{
				if (mcm.MethodBase.MethodHandle == new PickGold.Handler.HByReturnDelegate<Type>(this.GetType).Method.MethodHandle)//object::GetType()
					return new ReturnMessage(this.GetProxiedType(), null, 0, null, mcm);//object::GetType()

				var type = o.GetType();
				var method = mcm.MethodBase;
				if (type != method.DeclaringType && !type.IsSubclassOf(method.DeclaringType))
				{
					var types = new Type[ps.Length];
					for (var i = 0; i < ps.Length; i++)
						types[i] = ps[i].ParameterType;
					method = type.GetMethod(method.Name, BindingFlags.Public | BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy, null, types, null);
					if (method == null)
						method = type.GetMethod(mcm.MethodName, BindingFlags.Public | BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy);
				}
				o = method.Invoke(o, mcm.Args);
			}
			catch (Exception x)
			{
				Tracker.WriteError(x);
				var mrm = new ReturnMessage(x, mcm);
				if (ClassProxy.ProxyInvoke != null && e != null)
				{
					e.ReturnMessage = mrm;
					ClassProxy.ProxyInvoke(o, e);
				}
				return mrm;
			}

			var outArgs = new ArrayList();
			for (var i = 0; i < mcm.ArgCount; i++)
			{
				if (ps[i].ParameterType.IsByRef)
					outArgs.Add(mcm.Args[i]);
			}
			var rm = new ReturnMessage(o, outArgs.ToArray(), outArgs.Count, null, mcm);
			if (ClassProxy.ProxyInvoke != null && e != null)
			{
				e.ReturnMessage = rm;
				ClassProxy.ProxyInvoke(o, e);
			}
			return rm;
		}

		/// <summary>
		/// 取代理或创造代理
		/// </summary>
		/// <param name="type">代理类型，如果为空则表示取原型</param>
		/// <param name="proxy">原型或代理</param>
		/// <param name="deep">小于等于零表示取原型，一表示一次代理，大于表示多次重复代理</param>
		/// <returns>代理或原型</returns>
		protected static object GetProxy(Type type, object proxy, int deep)
		{
			if (proxy == null)
				return null;

			if (deep > 1)
			{
				if (ClassProxy.CreateProxy != null)
				{
					var e = new ProxyEventArgs(type);
					ClassProxy.CreateProxy(proxy, e);
					if (e.RealType != null)
						type = e.RealType;
				}
				var p = new ClassProxy(type, proxy);
				return p.GetTransparentProxy();
			}

			var cp = RemotingServices.GetRealProxy(proxy) as ClassProxy;
			if (cp != null)
			{
				if (deep > 0)
				{
					if (type == null)
						type = cp.GetProxiedType();
					proxy = cp._Proxy;
				}
				else
				{
					return cp._Proxy;
				}
			}
			if (deep > 0)
			{
				if (ClassProxy.CreateProxy != null)
				{
					var e = new ProxyEventArgs(type);
					ClassProxy.CreateProxy(proxy, e);
					if (e.RealType != null)
						type = e.RealType;
				}
				cp = new ClassProxy(type, proxy);
				return cp.GetTransparentProxy();
			}

			return null;
		}

		/// <summary>
		/// 取代理或创造代理
		/// </summary>
		/// <typeparam name="REAL">代理类型</typeparam>
		/// <param name="proxy">原型或代理</param>
		/// <param name="deep">小于等于零表示取原型，一表示一次代理，大于表示多次重复代理</param>
		/// <returns>代理或原型</returns>
		protected static REAL GetProxy<REAL>(object proxy, int deep)
		{
			proxy = ClassProxy.GetProxy(typeof(REAL), proxy, deep);
			if (proxy == null)
				return default(REAL);

			return (REAL)proxy;
		}

		/*
		/// <summary>
		/// 用作特定类型的哈希函数。
		/// </summary>
		/// <returns>当前实例的哈希代码。</returns>
		public override int GetHashCode()
		{
			if (this._Proxy == null)
				return base.GetHashCode();

			return this._Proxy.GetHashCode();
		}

		/// <summary>
		/// 返回表示当前实例的字符串。
		/// </summary>
		/// <returns>System.String，表示当前的实例。</returns>
		public override string ToString()
		{
			if (this._Proxy == null)
				return base.ToString();

			return this._Proxy.ToString();
		}
		*/

		/// <summary/>
		public static event ProxyEventHandler CreateProxy;

		/// <summary/>
		public static event InvokeMessageEventHandler ProxyInvoke;
	}

	/// <summary/>
	public class ProxyEventArgs : EventArgs
	{
		Type _RequestType;
		Type _RealType;

		/// <summary/>
		public ProxyEventArgs(Type requestType)
		{
			this._RequestType = requestType;
		}

		/// <summary/>
		public Type RequestType
		{
			get
			{
				return this._RequestType;
			}
		}

		/// <summary/>
		public Type RealType
		{
			set
			{
				if(value == null || value == this._RequestType || value.IsSubclassOf(this._RequestType))
				{
					this._RealType = value;
					return;
				}

				throw new ArgumentOutOfRangeException(MethodBase.GetCurrentMethod().Name, value, this.RequestType.AssemblyQualifiedName);
			}
			get
			{
				return this._RealType;
			}
		}
	}

	/// <summary/>
	public delegate void ProxyEventHandler(object sender, ProxyEventArgs e);

	/// <summary/>
	class InvokeEventArgs : EventArgs, IMethodCallMessage, IMethodReturnMessage, IMethodMessage, IMessage
	{
		IMethodCallMessage _MethodCall;
		Exception _Exception;
		object[] _OutArgs;
		object _ReturnValue;

		/// <summary/>
		public InvokeEventArgs(IMethodCallMessage methodCall)
		{
			this._MethodCall = methodCall;
			var ps = methodCall.MethodBase.GetParameters();
			int outArgCount = 0;
			for (int i = 0; i < ps.Length; i++)
			{
				if (ps[i].IsOut)
					outArgCount++;
			}
			if (outArgCount > 0)
				this._OutArgs = new object[outArgCount];
		}

		#region IMethodCallMessage 成员

		/// <summary/>
		public object GetInArg(int argNum)
		{
			return this._MethodCall.GetInArg(argNum);
		}

		/// <summary/>
		public string GetInArgName(int index)
		{
			return this._MethodCall.GetInArgName(index);
		}

		/// <summary/>
		public int InArgCount
		{
			get { return this._MethodCall.InArgCount; }
		}

		/// <summary/>
		public object[] InArgs
		{
			get { return this._MethodCall.InArgs; }
		}

		#endregion

		#region IMethodReturnMessage 成员

		/// <summary/>
		public Exception Exception
		{
			get
			{
				return this._Exception;
			}
			set
			{
				this._Exception = value;
			}
		}

		/// <summary/>
		public object GetOutArg(int argNum)
		{
			return this._OutArgs[argNum];
		}

		/// <summary/>
		public string GetOutArgName(int index)
		{
			var ps = this._MethodCall.MethodBase.GetParameters();
			for (int i = 0; i < ps.Length; i++)
			{
				if (index == 0)
					return ps[i].Name;

				if (ps[i].IsOut)
					index--;
			}
			return null;
		}

		/// <summary/>
		public int OutArgCount
		{
			get
			{
				if (this._OutArgs == null)
					return 0;

				return this._OutArgs.Length;
			}
		}

		/// <summary/>
		public object[] OutArgs
		{
			get { return this._OutArgs; }
		}

		/// <summary/>
		public object ReturnValue
		{
			get
			{
				return this._ReturnValue;
			}
			set
			{
				this._ReturnValue = value;
			}
		}

		#endregion

		#region IMethodMessage 成员

		/// <summary/>
		public int ArgCount
		{
			get { return this._MethodCall.ArgCount; }
		}

		/// <summary/>
		public object[] Args
		{
			get { return this._MethodCall.Args; }
		}

		/// <summary/>
		public object GetArg(int argNum)
		{
			return this._MethodCall.GetArg(argNum); ;
		}

		/// <summary/>
		public string GetArgName(int index)
		{
			return this._MethodCall.GetArgName(index);
		}

		/// <summary/>
		public bool HasVarArgs
		{
			get { return this._MethodCall.HasVarArgs; }
		}

		/// <summary/>
		public LogicalCallContext LogicalCallContext
		{
			get { return this._MethodCall.LogicalCallContext; }
		}

		/// <summary/>
		public MethodBase MethodBase
		{
			get { return this._MethodCall.MethodBase; }
		}

		/// <summary/>
		public string MethodName
		{
			get { return this._MethodCall.MethodName; }
		}

		/// <summary/>
		public object MethodSignature
		{
			get { return this._MethodCall.MethodSignature; }
		}

		/// <summary/>
		public string TypeName
		{
			get { return this._MethodCall.TypeName; }
		}

		/// <summary/>
		public string Uri
		{
			get { return this._MethodCall.Uri; }
		}

		#endregion

		#region IMessage 成员

		/// <summary/>
		public System.Collections.IDictionary Properties
		{
			get { return this._MethodCall.Properties; }
		}

		#endregion

		/// <summary/>
		public bool Invoked { set; get; }
	}

	/// <summary/>
	delegate void InvokeEventHandler(object sender, InvokeEventArgs e);
}
